package ru.inse.core.common;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public abstract class ObjectRegistrByKey<K, T>
{
    private static final Logger LOGGER = LoggerFactory.getLogger(ObjectRegistrByKey.class);
    private Map<K, T> registerMap = new HashMap<K, T>();
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    private ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
    private ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
    private String registerName;

    /**
     * Интерфейс для применения каких либо действий к объектам в регистре
     * @author evga
     *
     */
    public interface IAcceptor<K, T>
    {
        /**
         * @param object некий объект в регистре 
         * @return true если нужно перебирать объекты еще
         * @param key ключ по которому он зарегистрирован
         */
        boolean accept(K key, T object);
    }
    
    public ObjectRegistrByKey()
    {
        this("Object register by key");
    }

    public ObjectRegistrByKey(String registrName)
    {
        this.registerName = registrName;
    }

    public String getRegisterName()
    {
        return registerName;
    }

    /**
     * Регистрирует объект
     * @param object
     */
    public void register(K key, T object)
    {
        writeLock.lock();
        try
        {
            if (!registerMap.containsKey(key))
            {
                registerMap.put(key, object);
                LOGGER.debug(String.format("%s: Объект '%s' зарегестрирован под ID '%s'", registerName, object, key));
            }
            else
            {
                T existObject = registerMap.get(key);
                LOGGER.warn(String.format("%s: Объект под ID '%s' уже зарегестрирован, существующий '%s', новый '%s'", registerName, key, object, existObject));
                if (!existObject.equals(object))
                {
                    LOGGER.warn(String.format(registerName + ": Объекты не равны!!!"));
                }
            }
        }
        finally
        {
            writeLock.unlock();
        }
    }

    /**
     * Удаляет объект из регистра
     * @param provider
     */
    public void unregister(K key)
    {
        writeLock.lock();
        try
        {
            if (registerMap.remove(key) != null)
            {
                LOGGER.debug(String.format("$s: Объект '%s' удален с регистра", registerName, key));
            }
            else
            {
                LOGGER.warn(String.format("$s: Попытка удалить объект с регистра '%s' но он не зарегестрирован", registerName, key));
            }
        }
        finally
        {
            writeLock.unlock();
        }
    }

    
    public T getRegisteredObject(K key)
    {
        readLock.lock();
        try
        {
            return registerMap.get(key); 
        }
        finally
        {
            readLock.unlock();
        }
    }
    
    
    public void accept(IAcceptor<K, T> acceptor)
    {
        readLock.lock();
        try
        {
            for (Map.Entry<K, T> entry : registerMap.entrySet())
            {
                if (!acceptor.accept(entry.getKey(), entry.getValue()))
                {
                    break;
                }
            }
        }
        finally
        {
            readLock.unlock();
        }
    }
}
